#include "Rectangle.h"
#include "Circle.h"

Rectangle::Rectangle(const Point2& pos, const double& width, const double& height) : Shape(pos), width_(width), height_(height) {}

Rectangle* Rectangle::getInstance(const Point2& pos, const double& width, const double& height){
    return new Rectangle(pos, width, height);
}

const bool Rectangle::contains(const Point2& p) const{
    double x = getPos().getX();
    double y = getPos().getY();
    return (x <= p.getX() && p.getX() <= x + width_ && y <= p.getY() && p.getY() <= y + height_);
}

const bool Rectangle::overlaps(const Rectangle* r) const{
    return !(getPos().getX() > r->getPos().getX() + r->getWidth() || r->getPos().getX() > getPos().getX() + width_ ||
            getPos().getY() > r->getPos().getY() + r->getHeight() || r->getPos().getY() > getPos().getY() + height_);
}

const bool Rectangle::overlaps(const Circle* c) const{
    if (contains(c->getPos())){
        return true;}
    else if (contains(c->getPos() + Vector2(getWidth()/2, 0)))
        return true;
    else if (contains(c->getPos() + Vector2(-getWidth()/2, 0)))
        return true;
    else if (contains(c->getPos() + Vector2(0, getHeight()/2)))
        return true;
    else if (contains(c->getPos() + Vector2(0, -getHeight()/2)))
        return true;
    else if (c->contains(topLeft()))
        return true;
    else if (c->contains(topRight()))
        return true;
    else if (c->contains(bottomLeft()))
        return true;
    else if (c->contains(bottomLeft()))
        return true;
    else
        return false;
}

const bool Rectangle::overlaps(const Shape* other) const{
    const std::type_info& ti = typeid(*other);
    if (ti == typeid(Rectangle))
        return overlaps(dynamic_cast<const Rectangle*>(other));
    else if (ti == typeid(Circle))
        return overlaps(dynamic_cast<const Circle*>(other));
    else
        throw std::invalid_argument("Unknown collision shape");
}

double Rectangle::getArea() const{
    return width_ * height_;
}

const Rectangle* Rectangle::boundingRect() const{
    return getInstance(getPos(), width_, height_);
}

const Circle* Rectangle::boundingCircle() const{
    return Circle::getInstance(getPos(), (getPos() - topLeft()).magnitude());
}

const double& Rectangle::getWidth() const{
    return width_;
}
const double& Rectangle::getHeight() const{
    return height_;
}

const Point2 Rectangle::topLeft() const{
    return getPos() + 0.5 * Vector2(-width_, -height_);
}
const Point2 Rectangle::topRight() const{
    return getPos() + 0.5 * Vector2(width_, -height_);
}
const Point2 Rectangle::bottomLeft() const{
    return getPos() + 0.5 * Vector2(-width_, height_);
}
const Point2 Rectangle::bottomRight() const{
    return getPos() + 0.5 * Vector2(width_, height_);
}
